package splitfile

import (
	"context"
	"errors"
	"os"
	"path/filepath"
	"sieve_admin_server_userpass/lib/app"
	tt "sieve_admin_server_userpass/lib/util/time"
	"sort"
	"strings"
	"time"
)

const (
	SplitDaily   = "Ymd"
	SplitMonthly = "Ym"
	SplitNone    = ""
)

type Cfg struct {
	Dir      string
	FileName string
	Split    string
	Reserve  int
}

type File struct {
	f      *os.File
	suffix string
	cfg    *Cfg
	ctx    context.Context
	cancel context.CancelFunc
}

func New(cfg *Cfg) (*File, error) {
	if cfg == nil {
		return nil, errors.New("cfg required")
	}
	f := &File{cfg: cfg}
	if err := f.setFile(f.getSuffix()); err != nil {
		return nil, err
	}
	f.ctx, f.cancel = context.WithCancel(app.CTX)
	app.Async(f.checkSuffix)
	return f, nil
}

func (f *File) getSuffix() string {
	if f.cfg.Split == "" {
		return ""
	}
	return "." + tt.FmtDate(time.Now(), f.cfg.Split)
}

func (f *File) checkSuffix() {
	t := time.NewTicker(time.Second)
	defer t.Stop()
	for {
		select {
		case <-t.C:
			if suf := f.getSuffix(); suf != f.suffix {
				f.setFile(suf)
			}
		case <-f.ctx.Done():
			return
		}
	}
}

func (f *File) setFile(suffix string) error {
	f.suffix = suffix
	if err := os.MkdirAll(f.cfg.Dir, 0755); err != nil {
		return err
	}
	ff, err := os.OpenFile(filepath.Join(f.cfg.Dir, f.cfg.FileName+f.suffix), os.O_RDWR|os.O_CREATE|os.O_APPEND, os.FileMode(0666))
	if err != nil {
		return err
	}
	old := f.f
	f.f = ff
	if old != nil {
		old.Close()
	}
	if f.cfg.Reserve > 0 {
		rows, err := os.ReadDir(f.cfg.Dir)
		if err != nil {
			return err
		}
		list := []string{}
		for _, r := range rows {
			if r.IsDir() {
				continue
			}
			if strings.HasPrefix(r.Name(), f.cfg.FileName+".") {
				list = append(list, r.Name())
			}
		}
		sort.Strings(list)
		if len(list) > f.cfg.Reserve {
			for _, r := range list[:len(list)-f.cfg.Reserve] {
				os.Remove(filepath.Join(f.cfg.Dir, r))
			}
		}
	}
	return nil
}

var ErrFileNotInit = errors.New("file requires init")

func (f *File) Write(b []byte) (n int, err error) {
	ff := f.f
	if ff == nil {
		return 0, ErrFileNotInit
	}
	return ff.Write(b)
}

func (f *File) Close() error {
	ff := f.f
	if ff == nil {
		return ErrFileNotInit
	}
	f.f = nil
	f.cancel()
	return ff.Close()
}
